#region using

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using FluentDBC.Default;
using FluentDBC.DomainModel;
using FluentDBC.Extensions;

#endregion using

namespace FluentDBC
{
    public class FluentDBCConfiguration : IFluentDBCConfiguration
    {
        #region Singleton

        private static volatile FluentDBCConfiguration _instance;
        private static readonly object SyncRoot = new Object();

        public static IFluentDBCConfiguration GetInstance()
        {
            if (_instance == null)
            {
                lock (SyncRoot)
                {
                    if (_instance == null)
                        _instance = new FluentDBCConfiguration();
                }
            }

            return _instance;
        }

        #endregion Singleton

        #region private variables, fields, and ctors

        private IDictionary<string, Type> _contractActions { get; set; }

        public ISet<Class> ClassContracts { get; set; }

        private FluentDBCConfiguration()
        {
            ClassContracts = new HashSet<Class>();
            _contractActions = new ContractActionDefaultSetting(new Dictionary<string, Type>()).GetDefaultSettings();
        }

        #endregion private variables, fields, and ctors

        #region contract action functions

        public void RegisterContractAction(IDictionary<string, Type> contractActions)
        {
            _contractActions = contractActions;
        }

        public void RegisterContractAction(string key, Type value, bool isOverride)
        {
            if (_contractActions.ContainsKey(key) && isOverride)
            {
                var isSuccessed = _contractActions.Remove(key);

                if (isSuccessed)
                {
                    _contractActions.Add(key, value);
                }
            }
            else
            {
                _contractActions.Add(key, value);
            }
        }

        public void ClearAllContractActions()
        {
            _contractActions.Clear();
        }

        public Type GetContractAction(string key)
        {
            Type outValue;
            _contractActions.TryGetValue(key, out outValue);

            return outValue;
        }

        #endregion contract action functions

        #region api functions

        public IFieldContract<TModel> WithClass<TModel>()
        {
            ClassContracts.Add(new Class { Type = typeof(TModel) });

            return new FieldContract<TModel>(this, typeof(TModel));
        }

        public void ValidateContractForClass(dynamic obj)
        {
            Type myType = obj.GetType();

            var classObj = GetClass(myType);

            var myFields = myType.GetProperties();

            foreach (var field in classObj.Fields)
            {
                var fieldInfo = GetFieldInfo(field, myFields);
                var value = fieldInfo.GetValue(obj, null);

                foreach (var validationContractAction in field.ValidationContractActions)
                {
                    validationContractAction.ValidationContract(value);
                }
            }
        }

        #endregion api functions

        #region private functions

        private Class GetClass(Type type)
        {
            return ClassContracts.FirstOrDefault(classContract => classContract.Type.TypeCompare(type));
        }

        private static PropertyInfo GetFieldInfo(Field field, IEnumerable<PropertyInfo> fieldInfos)
        {
            return fieldInfos.FirstOrDefault(propertyInfo => propertyInfo.Name.CultureEquals(field.FieldName));
        }

        #endregion private functions
    }
}